package org.nimi317.web_gis.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.nimi317.web_gis.context.SecurityContextHolder;
import org.nimi317.web_gis.exception.E;
import org.nimi317.web_gis.exception.RException;
import org.nimi317.web_gis.dao.TableRangeMapper;
import org.nimi317.web_gis.entity.TableRange;
import org.nimi317.web_gis.service.ICurrentService;
import org.nimi317.web_gis.service.ITableRangeService;
import org.nimi317.web_gis.task.AirQualityRealTask;
import org.nimi317.web_gis.utils.TimeUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

/**
 * @author thunderobot
 */
@Service
@Slf4j
public class ITableRangeServiceImpl extends ServiceImpl<TableRangeMapper, TableRange> implements ITableRangeService {

    private final JdbcTemplate template;

    private final ICurrentService currentService;

    private final AirQualityRealTask airQualityRealTask;

    public ITableRangeServiceImpl(JdbcTemplate template, @Lazy ICurrentService currentService, @Lazy AirQualityRealTask airQualityRealTask) {
        this.template = template;
        this.currentService = currentService;
        this.airQualityRealTask = airQualityRealTask;
    }

    @Override
    @Transactional
    public void updateCurrentRange(String tableName) {
        TableRange id = this.getById(tableName);
        LocalDateTime time = SecurityContextHolder.getLastTime();
        id.setTo(time);
        this.saveOrUpdate(id);
    }

    @Override
    @Transactional
    public List<String> getAllTableName(LocalDateTime from, LocalDateTime to) {
        LambdaQueryWrapper<TableRange> wrapper = new LambdaQueryWrapper<>();
        wrapper
                .select(TableRange::getTableName)
                .le(TableRange::getFrom, to)
                .and(i -> i
                        .ge(TableRange::getTo, from));
        return this.list(wrapper).stream().map(TableRange::getTableName).toList();
    }

    @Override
    @Transactional
    public void createTableAndAddRange(String tableName) {
        LambdaQueryWrapper<TableRange> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TableRange::getTableName, tableName);
        if (this.exists(wrapper)) {
            throw new RException(E.TableRangeAddException);
        }
        template.execute("create table if not exists " + tableName.trim() +
                         "(" +
                         "    `station_code` varchar(20) not null," +
                         "    `timestamp`    datetime    not null," +
                         "    `CO`           varchar(10) not null," +
                         "    `NO2`          varchar(10) not null," +
                         "    `O3`           varchar(10) not null," +
                         "    `PM10`         varchar(10) not null," +
                         "    `PM25`        varchar(10) not null," +
                         "    `SO2`          varchar(10) not null," +
                         "    `AQI`          varchar(10) not null," +
                         "    `o3_8h`      varchar(10)       not null," +
                         "    `pm10_24h`   varchar(10)       not null," +
                         "    `pm25_24h`   varchar(10)       not null," +
                         "    `so2_24h`     varchar(10)       not null," +
                         "    `co_level`    tinyint    not null," +
                         "    `no2_level`   tinyint    not null," +
                         "    `o3_level`    tinyint    not null," +
                         "    `pm10_level` tinyint    not null," +
                         "    `pm25_level` tinyint    not null," +
                         "    `so2_level`  tinyint    not null," +
                         "    primary key (timestamp, station_code)" +
                         ");");
        TableRange entity = new TableRange();
        entity.setTableName(tableName);
        LocalDateTime dateTime = airQualityRealTask.getUpdate() ? TimeUtils.getNextCurrentUsefulDateTime() : TimeUtils.getCurrentUsefulDateTime();
        entity.setFrom(dateTime);
        entity.setTo(dateTime);
        this.save(entity);
        currentService.updateCurrentDB(tableName);
    }
}
